/********************************** (C) COPYRIGHT *******************************
 * File Name          : gattprofile.C
 * Author             : WCH
 * Version            : V1.0
 * Date               : 2018/12/10
 * Description        : 自定义包含五种不同属性的服务，包含可读、可写、通知、可读可写、安全可读
 * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
 * SPDX-License-Identifier: Apache-2.0
 *******************************************************************************/

/*********************************************************************
 * INCLUDES
 */
#include <CONFIG.h>
#include "gattprofile.h"

/*********************************************************************
 * MACROS
 */

/*********************************************************************
 * CONSTANTS
 */

// Position of shutterProfilechar4 value in attribute array
#define SHUTTERPROFILE_CHAR4_VALUE_POS    5

/*********************************************************************
 * TYPEDEFS
 */

/*********************************************************************
 * GLOBAL VARIABLES
 */

const uint8_t shutterProfileServUUID[ATT_UUID_SIZE] = { 0x66, 0x9A, 0x0C, 0x20,
        0x00, 0x08, 0x63, 0xAA, 0xEC, 0x11, 0xCC, 0xD1, 0xD0, 0xE0, 0xCC, 0xF9 };

const uint8_t shutterProfilecharControlUUID[ATT_UUID_SIZE] = { 0x66, 0x9A, 0x0C, 0x20,
        0x00, 0x08, 0x63, 0xAA, 0xEC, 0x11, 0xCC, 0xD1, 0xD2, 0xE0, 0xCC, 0xF9 };

const uint8_t shutterProfilecharNotifyUUID[ATT_UUID_SIZE] = { 0x66, 0x9A, 0x0C, 0x20,
        0x00, 0x08, 0x63, 0xAA, 0xEC, 0x11, 0xCC, 0xD1, 0xD1, 0xE0, 0xCC, 0xF9 };

static shutterProfileCBs_t *shutterProfile_AppCBs = NULL;

/*********************************************************************
 * Profile Attributes - variables
 */

// Simple Profile Service attribute
static const gattAttrType_t shutterProfileService = { ATT_UUID_SIZE,
        shutterProfileServUUID };

// Simple Profile Characteristic 1 Properties
//static uint8_t shutterProfileCharControlProps = GATT_PROP_READ | GATT_PROP_WRITE;
static uint8_t shutterProfileCharControlProps = GATT_PROP_WRITE;

// Characteristic 1 Value
static uint8_t shutterProfileCharControl[SHUTTERPROFILE_CHAR_CONTROL_LEN] = { 0 };

// Simple Profile Characteristic 1 User Description
static uint8_t shutterProfileCharControlUserDesp[] = "Control\0";

// Simple Profile Characteristic 4 Properties
static uint8_t shutterProfileCharNotifyProps = GATT_PROP_NOTIFY;

// Characteristic 4 Value
static uint8_t shutterProfileCharNotify[SHUTTERPROFILE_CHAR_NOTIFY_LEN] = { 0 };

// Simple Profile Characteristic 4 Configuration Each client has its own
// instantiation of the Client Characteristic Configuration. Reads of the
// Client Characteristic Configuration only shows the configuration for
// that client and writes only affect the configuration of that client.
static gattCharCfg_t shutterProfileCharNotifyConfig[4];

// Simple Profile Characteristic 4 User Description
static uint8_t shutterProfileCharNotifyUserDesp[] = "Report\0";

/*********************************************************************
 * Profile Attributes - Table
 */

static gattAttribute_t shutterProfileAttrTbl[] = {
// Simple Profile Service
        {
            { ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */
            GATT_PERMIT_READ, /* permissions */
            0, /* handle */
            (uint8_t *) &shutterProfileService /* pValue */
        },

        // Characteristic 1 Declaration
        {
            { ATT_BT_UUID_SIZE, characterUUID },
            GATT_PERMIT_READ,
            0,
            &shutterProfileCharControlProps
        },

        // Characteristic Value 1
        {
            { ATT_UUID_SIZE, shutterProfilecharControlUUID },
            GATT_PERMIT_WRITE,
            0,
            shutterProfileCharControl
        },

        // Characteristic 1 User Description
        {
            { ATT_BT_UUID_SIZE, charUserDescUUID },
            GATT_PERMIT_READ,
            0,
            shutterProfileCharControlUserDesp
        },

        // Characteristic 4 Declaration
        {
            { ATT_BT_UUID_SIZE, characterUUID },
            GATT_PERMIT_READ,
            0,
            &shutterProfileCharNotifyProps
        },

        // Characteristic Value 4
        {
            { ATT_UUID_SIZE, shutterProfilecharNotifyUUID },
            0,
            0,
            shutterProfileCharNotify
        },

        // Characteristic 4 configuration
        {
            { ATT_BT_UUID_SIZE, clientCharCfgUUID },
            GATT_PERMIT_READ | GATT_PERMIT_WRITE,
            0,
            (uint8_t *) shutterProfileCharNotifyConfig
        },

        // Characteristic 4 User Description
        {
            { ATT_BT_UUID_SIZE, charUserDescUUID },
            GATT_PERMIT_READ,
            0,
            shutterProfileCharNotifyUserDesp
        }
};

/*********************************************************************
 * LOCAL FUNCTIONS
 */
static bStatus_t shutterProfile_ReadAttrCB(uint16_t connHandle,
        gattAttribute_t *pAttr, uint8_t *pValue, uint16_t *pLen,
        uint16_t offset, uint16_t maxLen, uint8_t method);
static bStatus_t shutterProfile_WriteAttrCB(uint16_t connHandle,
        gattAttribute_t *pAttr, uint8_t *pValue, uint16_t len, uint16_t offset,
        uint8_t method);

static void shutterProfile_HandleConnStatusCB(uint16_t connHandle,
        uint8_t changeType);

/*********************************************************************
 * PROFILE CALLBACKS
 */
// Simple Profile Service Callbacks
gattServiceCBs_t shutterProfileCBs = {
        shutterProfile_ReadAttrCB, // Read callback function pointer
        shutterProfile_WriteAttrCB, // Write callback function pointer
        NULL                       // Authorization callback function pointer
        };

/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/*********************************************************************
 * @fn      SimpleProfile_AddService
 *
 * @brief   Initializes the Simple Profile service by registering
 *          GATT attributes with the GATT server.
 *
 * @param   services - services to add. This is a bit map and can
 *                     contain more than one service.
 *
 * @return  Success or Failure
 */
bStatus_t ShutterProfile_AddService(uint32_t services) {
    uint8_t status = SUCCESS;

    // Initialize Client Characteristic Configuration attributes
    GATTServApp_InitCharCfg(INVALID_CONNHANDLE, shutterProfileCharNotifyConfig);

    // Register with Link DB to receive link status change callback
    linkDB_Register(shutterProfile_HandleConnStatusCB);

    if (services & SHUTTERPROFILE_SERVICE) {
        // Register GATT attribute list and CBs with GATT Server App
        status = GATTServApp_RegisterService(shutterProfileAttrTbl,
                GATT_NUM_ATTRS(shutterProfileAttrTbl),
                GATT_MAX_ENCRYPT_KEY_SIZE, &shutterProfileCBs);
    }
    return (status);
}

/*********************************************************************
 * @fn      SimpleProfile_RegisterAppCBs
 *
 * @brief   Registers the application callback function. Only call
 *          this function once.
 *
 * @param   callbacks - pointer to application callbacks.
 *
 * @return  SUCCESS or bleAlreadyInRequestedMode
 */
bStatus_t ShutterProfile_RegisterAppCBs(shutterProfileCBs_t *appCallbacks) {
    if (appCallbacks) {
        shutterProfile_AppCBs = appCallbacks;

        return (SUCCESS);
    } else {
        return (bleAlreadyInRequestedMode);
    }
}

/*********************************************************************
 * @fn      SimpleProfile_SetParameter
 *
 * @brief   Set a Simple Profile parameter.
 *
 * @param   param - Profile parameter ID
 * @param   len - length of data to right
 * @param   value - pointer to data to write.  This is dependent on
 *          the parameter ID and WILL be cast to the appropriate
 *          data type (example: data type of uint16_t will be cast to
 *          uint16_t pointer).
 *
 * @return  bStatus_t
 */
bStatus_t ShutterProfile_SetParameter(uint8_t param, uint8_t len, void *value) {
    bStatus_t ret = SUCCESS;
    switch (param) {
    case SHUTTERPROFILE_CHAR_CONTROL:
        if (len == SHUTTERPROFILE_CHAR_CONTROL_LEN) {
            tmos_memcpy(shutterProfileCharControl, value, SHUTTERPROFILE_CHAR_CONTROL_LEN);
        } else {
            ret = bleInvalidRange;
        }
        break;

    case SHUTTERPROFILE_CHAR_NOTIFY:
        if (len == SHUTTERPROFILE_CHAR_NOTIFY_LEN) {
            tmos_memcpy(shutterProfileCharNotify, value, SHUTTERPROFILE_CHAR_NOTIFY_LEN);
        } else {
            ret = bleInvalidRange;
        }
        break;

    default:
        ret = INVALIDPARAMETER;
        break;
    }

    return (ret);
}

/*********************************************************************
 * @fn      SimpleProfile_GetParameter
 *
 * @brief   Get a Simple Profile parameter.
 *
 * @param   param - Profile parameter ID
 * @param   value - pointer to data to put.  This is dependent on
 *          the parameter ID and WILL be cast to the appropriate
 *          data type (example: data type of uint16_t will be cast to
 *          uint16_t pointer).
 *
 * @return  bStatus_t
 */
bStatus_t ShutterProfile_GetParameter(uint8_t param, void *value) {
    bStatus_t ret = SUCCESS;
    switch (param) {
    case SHUTTERPROFILE_CHAR_CONTROL:
        tmos_memcpy(value, shutterProfileCharControl, SHUTTERPROFILE_CHAR_CONTROL_LEN);
        break;

    case SHUTTERPROFILE_CHAR_NOTIFY:
        tmos_memcpy(value, shutterProfileCharNotify, SHUTTERPROFILE_CHAR_NOTIFY_LEN);
        break;

    default:
        ret = INVALIDPARAMETER;
        break;
    }

    return (ret);
}

/*********************************************************************
 * @fn          shutterProfile_Notify
 *
 * @brief       Send a notification containing a heart rate
 *              measurement.
 *
 * @param       connHandle - connection handle
 * @param       pNoti - pointer to notification structure
 *
 * @return      Success or Failure
 */
bStatus_t shutterProfile_Notify(uint16_t connHandle, attHandleValueNoti_t *pNoti) {
    uint16_t value = GATTServApp_ReadCharCfg(connHandle, shutterProfileCharNotifyConfig);

    // If notifications enabled
    if (value & GATT_CLIENT_CFG_NOTIFY) {
        // Set the handle
        pNoti->handle = shutterProfileAttrTbl[SHUTTERPROFILE_CHAR4_VALUE_POS].handle;
        // Send the notification
        return GATT_Notification(connHandle, pNoti, FALSE);
    }
    return bleIncorrectMode;
}

/*********************************************************************
 * @fn          shutterProfile_ReadAttrCB
 *
 * @brief       Read an attribute.
 *
 * @param       connHandle - connection message was received on
 * @param       pAttr - pointer to attribute
 * @param       pValue - pointer to data to be read
 * @param       pLen - length of data to be read
 * @param       offset - offset of the first octet to be read
 * @param       maxLen - maximum length of data to be read
 *
 * @return      Success or Failure
 */
static bStatus_t shutterProfile_ReadAttrCB(uint16_t connHandle,
        gattAttribute_t *pAttr, uint8_t *pValue, uint16_t *pLen,
        uint16_t offset, uint16_t maxLen, uint8_t method) {
    bStatus_t status = SUCCESS;

    // If attribute permissions require authorization to read, return error
    if (gattPermitAuthorRead(pAttr->permissions)) {
        // Insufficient authorization
        return (ATT_ERR_INSUFFICIENT_AUTHOR);
    }

    // Make sure it's not a blob operation (no attributes in the profile are long)
    if (offset > 0) {
        return (ATT_ERR_ATTR_NOT_LONG);
    }

    if (pAttr->type.len == ATT_BT_UUID_SIZE) {
        // 16-bit UUID
        // Should never get here! (characteristics 3 and 4 do not have read permissions)
        *pLen = 0;
        status = ATT_ERR_ATTR_NOT_FOUND;
    } else {
        // 128-bit UUID
        uint32_t uuid = BUILD_UINT32(pAttr->type.uuid[12], pAttr->type.uuid[13],
                        pAttr->type.uuid[14], pAttr->type.uuid[15]);
        switch (uuid)
        {
          // No need for "GATT_SERVICE_UUID" or "GATT_CLIENT_CHAR_CFG_UUID" cases;
          // gattserverapp handles those reads

          // characteristic 4 does not have read permissions, but because it
          //   can be sent as a notification, it is included here
          case SHUTTERPROFILE_CHAR_NOTIFY_UUID:
            *pLen = SHUTTERPROFILE_CHAR_NOTIFY_LEN;
            tmos_memcpy( pValue, pAttr->pValue, SHUTTERPROFILE_CHAR_NOTIFY_LEN );
            break;

          default:
            // Should never get here! (characteristics 3 and 4 do not have read permissions)
            *pLen = 0;
            status = ATT_ERR_ATTR_NOT_FOUND;
            break;
        }
    }

    return (status);
}

/*********************************************************************
 * @fn      shutterProfile_WriteAttrCB
 *
 * @brief   Validate attribute data prior to a write operation
 *
 * @param   connHandle - connection message was received on
 * @param   pAttr - pointer to attribute
 * @param   pValue - pointer to data to be written
 * @param   len - length of data
 * @param   offset - offset of the first octet to be written
 *
 * @return  Success or Failure
 */
static bStatus_t shutterProfile_WriteAttrCB(uint16_t connHandle,
        gattAttribute_t *pAttr, uint8_t *pValue, uint16_t len, uint16_t offset,
        uint8_t method) {
    bStatus_t status = SUCCESS;
    uint8_t notifyApp = 0xFF;

    // If attribute permissions require authorization to write, return error
    if (gattPermitAuthorWrite(pAttr->permissions)) {
        // Insufficient authorization
        return (ATT_ERR_INSUFFICIENT_AUTHOR);
    }

    if (pAttr->type.len == ATT_BT_UUID_SIZE) {
        uint16_t uuid = BUILD_UINT16(pAttr->type.uuid[0], pAttr->type.uuid[1]);
        switch (uuid) {
        case GATT_CLIENT_CHAR_CFG_UUID:
            status = GATTServApp_ProcessCCCWriteReq(connHandle, pAttr, pValue,
                    len, offset, GATT_CLIENT_CFG_NOTIFY);
            break;
        default:
            status = ATT_ERR_ATTR_NOT_FOUND;
            break;
        }
    } else {
        // 128-bit UUID
        uint32_t uuid = BUILD_UINT32(pAttr->type.uuid[12], pAttr->type.uuid[13],
                pAttr->type.uuid[14], pAttr->type.uuid[15]);
        switch (uuid) {
        case SHUTTERPROFILE_CHAR_CONTROL_UUID:
            //Validate the value
            // Make sure it's not a blob oper
            if (offset == 0) {
                if (len > SHUTTERPROFILE_CHAR_CONTROL_LEN) {
                    status = ATT_ERR_INVALID_VALUE_SIZE;
                }
            } else {
                status = ATT_ERR_ATTR_NOT_LONG;
            }

            //Write the value
            if (status == SUCCESS) {
                tmos_memcpy(pAttr->pValue, pValue, SHUTTERPROFILE_CHAR_CONTROL_LEN);
                notifyApp = SHUTTERPROFILE_CHAR_CONTROL;
            }
            break;

        default:
            // Should never get here! (characteristics 2 and 4 do not have write permissions)
            status = ATT_ERR_ATTR_NOT_FOUND;
            break;
        }
    }

    // If a charactersitic value changed then callback function to notify application of change
    if ((notifyApp != 0xFF) && shutterProfile_AppCBs && shutterProfile_AppCBs->pfnShutterProfileChange) {
        shutterProfile_AppCBs->pfnShutterProfileChange(notifyApp, pValue, len);
    }

    return (status);
}

/*********************************************************************
 * @fn          shutterProfile_HandleConnStatusCB
 *
 * @brief       Simple Profile link status change handler function.
 *
 * @param       connHandle - connection handle
 * @param       changeType - type of change
 *
 * @return      none
 */
static void shutterProfile_HandleConnStatusCB(uint16_t connHandle,
        uint8_t changeType) {
    // Make sure this is not loopback connection
    if (connHandle != LOOPBACK_CONNHANDLE) {
        // Reset Client Char Config if connection has dropped
        if ((changeType == LINKDB_STATUS_UPDATE_REMOVED)|| ((changeType == LINKDB_STATUS_UPDATE_STATEFLAGS) && (!linkDB_Up(connHandle)))) {
            GATTServApp_InitCharCfg(connHandle, shutterProfileCharNotifyConfig);
        }
    }
}
